package com.zxy.ziems.server.energy.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zxy.btp.common.response.PageResult;
import com.zxy.btp.web.util.TokenUtils;
import com.zxy.ziems.server.board.pojo.vo.DosageVo;
import com.zxy.ziems.server.constant.DateType;
import com.zxy.ziems.server.device.mapper.DeviceMapper;
import com.zxy.ziems.server.device.pojo.entity.DeviceEntity;
import com.zxy.ziems.server.device.service.DeviceService;
import com.zxy.ziems.server.domain.DataTempEntity;
import com.zxy.ziems.server.dto.DataTempDto;
import com.zxy.ziems.server.energy.mapper.DataTempMapper;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataDayMapper;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataHistoryMapper;
import com.zxy.ziems.server.energy.pojo.dto.DeviceDataTypeDTO;
import com.zxy.ziems.server.energy.pojo.entity.DeviceMeasureDataDayEntity;
import com.zxy.ziems.server.energy.pojo.entity.DeviceMeasureDataHistoryEntity;
import com.zxy.ziems.server.energy.pojo.param.DeviceMeasureDataDay;
import com.zxy.ziems.server.energy.pojo.param.DeviceMeasureDataDayPageParam;
import com.zxy.ziems.server.energy.pojo.vo.DeviceMeasureDataDayVO;
import com.zxy.ziems.server.energy.service.DeviceMeasureDataDayService;
import com.zxy.ziems.server.energy.service.EnergyPriceService;
import com.zxy.ziems.server.mybatis.core.service.ServiceImplX;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.BusinessExceptionUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceMeasureDataDayServiceImpl extends ServiceImplX<DeviceMeasureDataDayMapper, DeviceMeasureDataDayEntity> implements DeviceMeasureDataDayService {

    @Autowired
    private DataTempMapper dataTempMapper;
    @Autowired
    DeviceMapper deviceMapper;
    @Autowired
    DeviceMeasureDataDayMapper deviceMeasureDataDayMapper;
    @Autowired
    DeviceMeasureDataHistoryMapper deviceMeasureDataHistoryMapper;
    @Autowired
    EnergyPriceService energyPriceService;
    @Autowired
    private DeviceService deviceService;

    @Override
    public List<DeviceDataTypeDTO> getDeviceDataTypeSum(String dateType, LocalDate startDate, LocalDate endDate, Set<String> deviceIds) {
        AssertUtil.nonNull(startDate, "开始时间不可为空");
        AssertUtil.nonNull(endDate, "截至时间不可为空");
        AssertUtil.isTrue(!CollectionUtils.isEmpty(deviceIds), "设备ID不可为空");
        List<DeviceDataTypeDTO> res = new ArrayList<>();
        if (dateType.equals(DateType.MONTH)) {
            res = baseMapper.deviceSum(startDate, endDate, deviceIds, "MONTH");
        } else if (dateType.equals(DateType.YEAR)) {
            res = baseMapper.deviceSum(startDate, endDate, deviceIds, "YEAR");
        } else {
            BusinessExceptionUtil.exception("不支持类型");
        }
        return res;
    }

    @Override
    public List<DosageVo> sumByDateTypeAndClassifyIds(List<String> classifyIds, String dataTypeId, LocalDate startDate, LocalDate endDate, String dateFormat) {
        return baseMapper.sumByDateTypeAndClassifyIds(classifyIds, dataTypeId, startDate, endDate, dateFormat);
    }


    /**
     * 数据修正接口，修正天级别数据
     * 1.根据dataTemp表临时处理天数据
     */
    @Override
    @Transactional
    public void dataProcessing() {

        List<DataTempEntity> dataTempList = dataTempMapper.selectList(null);
        List<String> tagNames = dataTempList.stream().map(DataTempEntity::getTagName).distinct().toList();
        QueryWrapper<DeviceEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("code", tagNames);
        // 获取tagName
        Map<String, String> deviceMap = deviceMapper.selectList(queryWrapper).stream()
                .collect(Collectors.toMap(DeviceEntity::getCode, DeviceEntity::getId));
        deviceMap.forEach((k, v) -> {
            System.out.println(k + "---" + v);
        });
        // 获取需要处理数据
        List<DataTempDto> dataTempDtoList = new ArrayList<>();
        dataTempList.forEach(dataTempEntity -> {
            DataTempDto dataTempDto = new DataTempDto();
            dataTempDto.setDeviceId(deviceMap.getOrDefault(dataTempEntity.getTagName(), "000"));
            dataTempDto.setTagName(dataTempEntity.getTagName());
            dataTempDto.setIncrementValue(dataTempEntity.getIncrementValue());
            dataTempDto.setIncrementCost(dataTempEntity.getIncrementCost());
            dataTempDto.setMeasureTime(dataTempEntity.getMeasureTime());
            dataTempDtoList.add(dataTempDto);
        });

        // 根据deviceId和measureTime修正day表中数据
        dataTempDtoList.forEach(dataTempDto -> {
            QueryWrapper<DeviceMeasureDataDayEntity> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.lambda()
                    .eq(DeviceMeasureDataDayEntity::getDeviceId, dataTempDto.getDeviceId())
                    .eq(DeviceMeasureDataDayEntity::getMeasureTime, dataTempDto.getMeasureTime());
            DeviceMeasureDataDayEntity DataDay = deviceMeasureDataDayMapper.selectOne(queryWrapper1);
            if (DataDay != null) {
                DataDay.setIncrementValue(dataTempDto.getIncrementValue());
                DataDay.setIncrementCost(dataTempDto.getIncrementCost());
                deviceMeasureDataDayMapper.updateById(DataDay);
            } else {
                log.info("未录入系统的电力监控点位为：" + dataTempDto.getTagName());
            }
        });

    }

    /**
     * 采集值正确时，修正增量值
     * 修正18号增量，输入“2025-03-18”
     * 1、根据date获取当前值和前一天值
     * 2、计算当前值和前一天值的差值
     * 3、更新当前值的增量值
     * 4、更新当前值的费用（根据情况增加费用计算）
     *
     * @param date
     */
    @Override
    @Transactional
    public void dataProcessingTemp(String date) {
        //先查询所有设备，获取设备id
        List<DeviceEntity> deviceList = deviceMapper.selectList(null);
        List<String> deviceIdList = deviceList.stream().map(DeviceEntity::getId).toList();
        // 根据设备id和日期，在DeviceMeasureDataDayEntity表中查询设备date日期数据和前一天数据
        LocalDate curDate = LocalDate.parse(date);
        LocalDate preDate = curDate.minusDays(1);

        // 查询出
        deviceIdList.forEach(deviceId -> {
            QueryWrapper<DeviceMeasureDataDayEntity> QueryWrapper = new QueryWrapper<>();
            QueryWrapper.lambda()
                    .eq(DeviceMeasureDataDayEntity::getDeviceId, deviceId)
                    .ge(DeviceMeasureDataDayEntity::getMeasureTime, preDate)
                    .le(DeviceMeasureDataDayEntity::getMeasureTime, curDate);
            List<DeviceMeasureDataDayEntity> dataList = deviceMeasureDataDayMapper.selectList(QueryWrapper);
            if (!CollectionUtils.isEmpty(dataList)) {
                Map<LocalDate, DeviceMeasureDataDayEntity> dataMap = dataList.stream().collect(Collectors.toMap(DeviceMeasureDataDayEntity::getMeasureTime, data -> data));
                DeviceMeasureDataDayEntity curData = dataMap.get(curDate);
                DeviceMeasureDataDayEntity preData = dataMap.get(preDate);
                // 计算差值
                if (ObjectUtil.isNotEmpty(preData) && ObjectUtil.isNotEmpty(curData)) {
                    curData.setIncrementValue(curData.getMeasureValue().subtract(preData.getMeasureValue()));
                    deviceMeasureDataDayMapper.updateById(curData);
                }
                // 没有更新费用，因为费用是有的
            }

        });
        log.info("=====dataProcessingTemp 运行完成-所有数据已更新完成====");
    }

    /**
     * 批量修改天级别数据
     * 修正17号输入“2025-03-17”
     * 根据history历史表修正天级别数据异常
     * 1.根据day表数据，获取上一周期（3月16号）数据，没有3月17号数据时使用
     * 2.根据history表数据，获取3月18日0点数据代表17号整天的数据
     * 3.根据上一周期数据和当前周期数据，修正day表数据
     *
     * @param date
     */
    @Override
    @Transactional
    public void modifyDayData(String date) {
        LocalDate curDate = LocalDate.parse(date);
        LocalDateTime dateTime = curDate.atStartOfDay();
        // 从day表，获取上一周期数据
        QueryWrapper<DeviceMeasureDataDayEntity> query = new QueryWrapper<>();
        query.lambda()
                .eq(DeviceMeasureDataDayEntity::getMeasureTime, curDate.minusDays(1));
        List<DeviceMeasureDataDayEntity> preDeviceMeasureDataDay = deviceMeasureDataDayMapper.selectList(query);
        List<String> deviceIds = new ArrayList<>(preDeviceMeasureDataDay.stream().map(DeviceMeasureDataDayEntity::getDeviceId).toList());
        // 手工维护的数据不做更新
        QueryWrapper<DeviceEntity> deviceQueryWrapper = new QueryWrapper<>();
        deviceQueryWrapper.lambda().eq(DeviceEntity::getTypeCode, "sgwh");
        List<DeviceEntity> deviceList = deviceMapper.selectList(deviceQueryWrapper);
        if (!deviceList.isEmpty()) {
            Set<String> deviceIdSet = deviceList.stream().map(DeviceEntity::getId).collect(Collectors.toSet());
            preDeviceMeasureDataDay.removeIf(deviceMeasureDataDayEntity -> deviceIdSet.contains(deviceMeasureDataDayEntity.getDeviceId()));
            deviceIds.removeAll(deviceIdSet);
        }
        // 从history表，获取当前周期指定设备的数据
        List<DeviceMeasureDataHistoryEntity> historyEntityList = deviceMeasureDataHistoryMapper.selectHourDataFirst(deviceIds, dateTime.plusDays(1).minusHours(2), dateTime.plusDays(1).plusHours(2));
        Map<String, DeviceMeasureDataHistoryEntity> deviceIdHistoryMap =
                historyEntityList.stream().collect(Collectors.toMap(DeviceMeasureDataHistoryEntity::getDeviceId, v -> v, (existing, replacement) -> existing));
        log.info("history表，获取当前周期指定设备的数据已完成");

        List<DeviceMeasureDataDayEntity> deviceMeasureDataDayList = new ArrayList<>();
        // 存当前周期数据
        preDeviceMeasureDataDay.forEach(preData -> {
            DeviceMeasureDataDayEntity deviceMeasureDataDayEntity = new DeviceMeasureDataDayEntity();
            deviceMeasureDataDayEntity.setDeviceId(preData.getDeviceId());
            deviceMeasureDataDayEntity.setDataTypeId(preData.getDataTypeId());
            deviceMeasureDataDayEntity.setUnitId(preData.getUnitId());
            if (deviceIdHistoryMap.containsKey(preData.getDeviceId())) {
                deviceMeasureDataDayEntity.setMeasureValue(deviceIdHistoryMap.get(preData.getDeviceId()).getMeasureValue());
                deviceMeasureDataDayEntity.setIncrementValue(deviceIdHistoryMap.get(preData.getDeviceId()).getMeasureValue().subtract(preData.getMeasureValue()));
            } else {
                deviceMeasureDataDayEntity.setMeasureValue(preData.getMeasureValue());
                deviceMeasureDataDayEntity.setIncrementValue(BigDecimal.ZERO);
            }
            setDayPrice(dateTime, preData.getDataTypeId(), deviceMeasureDataDayEntity);
            deviceMeasureDataDayEntity.setMeasureTime(curDate);
            deviceMeasureDataDayList.add(deviceMeasureDataDayEntity);
        });
        deviceMeasureDataDayMapper.insertBatch(deviceMeasureDataDayList);
        log.info("==" + curDate + "的数据修正已完成====");
    }

    /**
     * 设置小时值的费用，根据dataType值进行设置费用
     *
     * @param date
     * @param dataTypeId
     * @param entity
     */
    private void setDayPrice(LocalDateTime date, String dataTypeId, DeviceMeasureDataDayEntity entity) {
        BigDecimal incrementValue = entity.getIncrementValue();
        BigDecimal price = energyPriceService.getEnergyPriceByDataTypeId(dataTypeId, date);
        if (price != null) {
            entity.setIncrementCost(incrementValue.multiply(price));
        }
    }


    public PageResult<DeviceMeasureDataDayVO> getPageList(DeviceMeasureDataDayPageParam param) {
        // 参数校验
        if (param == null
                || param.getStartTime() == null
                || param.getEndTime() == null) {
            throw new IllegalArgumentException("参数不能为空或包含空值");
        }
        try {

            List<String> deviceIds = deviceMapper.selectDeviceIdByCodeOrName(param.getCode(),param.getName());

            // 调用分页查询方法
            PageResult<DeviceMeasureDataDayEntity> result = customPage(param,
                    (page, aParam) -> baseMapper.queryPageListByDeviceId(
                            page,
                            aParam.getStartTime(),
                            aParam.getEndTime(),
                            deviceIds
                    )
            );
            // 如果结果列表为空，直接返回空分页结果
            if (CollectionUtil.isEmpty(result.getList())) {
                return new PageResult<>(0L, Collections.emptyList());
            }

            // 将 Entity 转换为 VO（视图对象）
            List<DeviceMeasureDataDayVO> list = result.getList().stream()
                    .map(entity -> {
                        DeviceMeasureDataDayVO vo = new DeviceMeasureDataDayVO();
                        BeanUtils.copyProperties(entity, vo);
                        if (deviceService != null) {
                            DeviceEntity device = deviceService.getById(entity.getDeviceId());
                            if (device != null) {
                                vo.setDeviceCode(device.getCode());
                                vo.setDeviceName(device.getName());
                            }
                        }
                        return vo;
                    })
                    .collect(Collectors.toList());

            return new PageResult<>(result.getTotal(), list);
        } catch (
                Exception e) {
            // 异常处理，记录日志并抛出自定义异常
            log.error("查询分页数据失败", e);
            throw new RuntimeException("查询分页数据失败", e);
        }
    }

    public boolean  updateMeasureDayData(DeviceMeasureDataDay data) {

        if (data.getDeviceId() == null || data.getMeasureTime() == null || data.getUpdateTime() == null) {
            throw new IllegalArgumentException("设备ID、测量时间和更新时间不能为空");
        }
        int updatedRows = deviceMeasureDataDayMapper.updateMeasureDataByDeviceAndTime(data.getDeviceId(),
                data.getMeasureTime(),
                data.getMeasureValue(),
                data.getAvgValue(),
                data.getIncrementValue(),
                data.getIncrementCost(),
                TokenUtils.getUserIdByToken(),
                data.getUpdateTime() );// 传入当前数据库中的 update_time
        if (updatedRows == 0) {
            throw new RuntimeException("数据已被修改，请刷新后重试");
        }
        return true;

    }
}
